//    Copyright (C) Kherty.  All rights reserved.
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows.Media;

namespace OpenLS.Core.Formatting
{
    sealed class FormatParser
    {
        public FormatParser(string text)
        {
            _text = text;
            switch (text.ToLower())
            {
                case "general":
                case "g":
                    {
                        Tokens.Add(new GeneralToken());
                        return;
                    }


            }
            if (text.ToLower().StartsWith("general"))
            {
                Tokens.Add(new GeneralToken());
                return;
            }
            _currentText = text;
            _currentPosition = 0;
            while (CurrentChar != '\0')
            {
                if (isNumeric(CurrentChar))
                {
                    {
                        var sb = new StringBuilder();
                        while (isNumeric(CurrentChar))
                        {
                            sb.Append(CurrentChar.ToString());
                            moveNext();
                        }
                        Tokens.Add(new NumericToken(sb.ToString()));
                    }
                    continue;
                }
                if (isCurrency(CurrentChar))
                {
                    var sb = new StringBuilder();
                    while (isCurrency(CurrentChar))
                    {
                        sb.Append(CurrentChar.ToString());
                        moveNext();
                    }
                    Tokens.Add(new CurrencyToken(sb.ToString()));
                    continue;
                }
                if (isLiteral(CurrentChar))
                {
                    var sb = new StringBuilder();
                    while (isLiteral(CurrentChar))
                    {
                        sb.Append(CurrentChar.ToString());
                        moveNext();
                    }
                    Tokens.Add(new LiteralToken(sb.ToString()));
                    continue;
                }
                switch (CurrentChar)
                {
                    case '@':
                        Tokens.Add(new ExplicitStringToken());
                        moveNext(); break;
                    case '%':
                        Tokens.Add(new PercentToken());
                        moveNext();
                        break;
                    case '*':
                        moveNext();
                        if (CurrentChar == '\0')
                            return;
                        Tokens.Add(new RepeatToken(CurrentChar));
                        moveNext();
                        break;
                    case '_':
                        moveNext();
                        if (CurrentChar == '\0')
                            return;
                        Tokens.Add(new InvisibleToken(CurrentChar));
                        moveNext();
                        break;
                    case '.':
                        Tokens.Add(new DotToken());
                        moveNext();
                        break;
                    case ';':
                        Tokens.Add(new ClauseSeparatorToken());
                        moveNext();
                        break;
                    case '/':
                        Tokens.Add(new SlashToken());
                        moveNext();
                        break;
                    case 'h':
                    case 's':
                    case 'd':
                    case 'm':
                    case 'y':
                        {
                            char c = CurrentChar.ToString().ToLower()[0];
                            var sb = new StringBuilder();
                            while (CurrentChar.ToString().ToLower()[0] == c)
                            {
                                sb.Append(CurrentChar);
                                moveNext();
                            }
                            Tokens.Add(new DateTimeToken(sb.ToString().Replace('h', 'H')));
                            break;
                        }
                    case 'Y':
                    case 'M':
                    case 'D':
                    case 'H':
                    case 'S':
                        {
                            char c = CurrentChar.ToString().ToLower()[0];
                            var sb = new StringBuilder();
                            while (CurrentChar.ToString().ToLower()[0] == c)
                            {
                                sb.Append(CurrentChar);
                                moveNext();
                            }
                            Tokens.Add(new DateTimeToken(sb.ToString().Replace('h', 'H')));
                            break; 
                        }
                    case 'a':
                    case 'A':
                        int newPos = _currentPosition + 1;
                        if (this[newPos].ToString().ToLower()[0] == 'm')
                            newPos++;
                        if (this[newPos].ToString().ToLower()[0] == '/')
                            newPos++;
                        if (this[newPos].ToString().ToLower()[0] == 'p')
                            newPos++;
                        if (this[newPos].ToString().ToLower()[0] == 'm')
                            newPos++;
                        Tokens.Add(new AmPmToken());
                        _currentPosition = newPos + 1;
                        break;


                    case '[':
                        {
                            int i = _currentText.IndexOfAny(new[] { ']', ';' }, _currentPosition + 1);
                            if (i == -1)
                                i = _currentText.Length;
                            string s = _currentText.Substring(_currentPosition + 1, i - _currentPosition - 1);
                            Color color;
                            _currentPosition = i + 1;
                            if (GetColor(s, out color))
                            {
                                Tokens.Add(new ColorToken(color));
                                break;
                            }
                            if (s.ToLower() == "h")
                            {
                                Tokens.Add(new TotalHoursToken());
                                break;
                            }
                            if (s.Length == 0)
                                break;
                            if (s[0] == '$')
                            {
                                int dashIndex = s.IndexOf('-');
                                string currency = s;
                                if (dashIndex > 0)
                                    currency = s.Substring(1, dashIndex - 1);
                                if (currency.Length == 0)
                                    currency = "$";
                                if (currency.Length > 1 && currency.StartsWith("$"))
                                    currency = currency.Substring(1);
                                Tokens.Add(new CurrencyToken(currency));
                                break;
                            }
                                if(s[0] ==  '<')
                                {
                                    int l1 = 1;

                                    if (s[1] == '=')
                                        l1++;
                                    Tokens.Add(new ConditionToken(s));
                                    break;
                                }
                                if (s[0] == '>')
                                {
                                    int ll1 = 1;

                                    if (s[1] == '=')
                                        ll1++;
                                    Tokens.Add(new ConditionToken(s));
                                    break;
                                }
                                Debug.WriteLine("Unhandled " + s);
                            break;
                        }
                    case '"':
                        {
                            int i = _currentText.IndexOfAny(new[] { '"' }, _currentPosition + 1);
                            if (i == -1)
                                i = _currentText.Length;
                            string s = _currentText.Substring(_currentPosition + 1, i - _currentPosition - 1);
                            Tokens.Add(new LiteralToken(s));
                            _currentPosition = i + 1;
                            break;
                        }
                    case '\\':
                        {
                            moveNext();
                            if (CurrentChar == '\0')
                                return;
                            Tokens.Add(new LiteralToken(CurrentChar.ToString()));
                            moveNext();
                            break;
                        }
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        {
                            var sb = new StringBuilder();
                            while (char.IsDigit(CurrentChar))
                            {
                                sb.Append(CurrentChar.ToString());
                                moveNext();
                            }
                            Tokens.Add(new IntegerToken(int.Parse(sb.ToString())));
                            break;
                        }
                    default:
                        Tokens.Add(new UnhandledToken(CurrentChar));
                        moveNext();
                        break;
                }

            }
        }
        internal sealed class UnhandledToken : FormatToken
        {
            public UnhandledToken(char c)
            {
                this._c = c;
            }

            readonly char _c;

            public override int Length
            {
                get { return 1; }
            }

            internal override string Code
            {
                get { return _c.ToString(); }
            }
        }

        readonly string _text;

        static bool GetColor(string name, out Color color)
        {
            switch (name.ToLower())
            {
                case "red": color = Colors.Red; return true;
                case "black": color = Colors.Black; return true;
                case "blue": color = Colors.Blue; return true;
                case "cyan": color = Colors.Cyan; return true;
                case "green": color = Colors.Green; return true;
                case "magenta": color = Colors.Magenta; return true;
                case "white": color = Colors.White; return true;
                case "yellow": color = Colors.Yellow; return true;
            }
            color = Colors.Black;
            return false;
        }
        internal readonly List<FormatToken> Tokens = new List<FormatToken>();
        readonly string _currentText;
        int _currentPosition;
        char CurrentChar
        {
            get
            {
                return this[_currentPosition];
            }
        }
        char this[int i]
        {
            get
            {
                if (i >= _currentText.Length)
                    return '\0';
                return _currentText[i];
            }
        }
        void moveNext()
        {
            _currentPosition++;
        }
        static bool isNumeric(char c)
        {
            switch (c)
            {
                case '#':
                case '0':
                case '?':
                    return true;
            }
            return false;
        }
        static bool isCurrency(char c)
        {
            switch (c)
            {
                case '$': //\\
                case '£':
                case '€':
                case '¥':
                case '￥':
                 
                    return true;
            }
            return false;
        }
        static bool isLiteral(char c)
        {
            switch (c)
            {
                case ':':
                case ' ':
                case '-':
                case '+':
                case 'e':
                case 'E':
                case ',':
                //\\case '$':
                case '(':
                case ')':
                    return true;
            }
            return false;
        }


        internal FormatCategory GetCategory()
        {
            bool hasNumber = false;
            bool hasSlash = false;
            bool hasExplicitString = false;
            bool hasE = false;
            bool hasCurrency = false;
            foreach (FormatToken token in Tokens)
            {
                if (token is GeneralToken)
                    return FormatCategory.General;
                if (token is DateTimeToken)
                    return FormatCategory.Date;//\\ or time?
                if (token is CurrencyToken)
                    hasCurrency = true;
                if (token is LiteralToken)
                {
                    string s = ((LiteralToken) token).Code;
                    switch (s)
                    {
                        case "$":
                        case "£":
                        case "₤":
                        case "¥":
                        case "€":
                            hasCurrency = true;
                            break;
                    }
                }
                //\\return FormatCategory.Currency;
                if (token is NumericToken)
                    hasNumber = true;
                if (token is SlashToken)
                    hasSlash = true;
                if (token is PercentToken)
                    return FormatCategory.Percent;
                if (token is ExplicitStringToken)
                    hasExplicitString = true;
                if (token is LiteralToken && !hasE)
                    hasE = token.Code.ToUpper().Contains("E");

            }
            if (hasCurrency)
                return FormatCategory.Currency;
            if (hasNumber)
            {
                if (hasSlash)
                    return FormatCategory.Fraction;
                if (hasE)
                    return FormatCategory.Scientific;
                return FormatCategory.Number;
            }
            if (hasExplicitString)
                return FormatCategory.Text;
            Debug.WriteLine("Warning: unable to understand " + _text);
            return FormatCategory.General;
        }
    }
}
